home *** CD-ROM | disk | FTP | other *** search
/ Network CD 1 / Network CD.iso / others / emit / emit20.lzh / emit.c < prev    next >
C/C++ Source or Header  |  1988-01-30  |  18KB  |  603 lines

  1. /* --------------------------------------------------------------------
  2.  * NAME
  3.  *     Emit V2.0 by Justin V. McCormick 1/29/88
  4.  *
  5.  *    Placed by the Author in the Public Domain -- freely reproducable
  6.  *    in any form so long as this notice remains intact.
  7.  *
  8.  *     Some Rights Reserved (i.e., I will not be held accountable for the
  9.  *    use or misuse of this code).
  10.  *     
  11.  * SYNOPSIS
  12.  *     emit -[s][r] [filename]
  13.  *     -s    Send filename over serial port
  14.  *     -r    Receive data from serial port and store in "filename"        
  15.  *     
  16.  * DESCRIPTION
  17.  *     Emit is a high-speed serial transfer utility useful for moving
  18.  *     files from one Amiga to another via a null modem cable.  Emit
  19.  *     operates at 280,000 baud, and its actual data transfer rate has
  20.  *      been benchmarked at over 24,000 bytes per second.
  21.  *      
  22.  *     You should type "emit -s foo" on the sending Amiga to transmit
  23.  *     a file named "foo".  The sending Amiga will attempt to access
  24.  *     the file "foo", and await a handshake signal from the receiving
  25.  *     Amiga.  On the receiving Amiga, you should then type "emit -r foo"
  26.  *     to download the file.  As soon as the handshake has been
  27.  *     established, transfer of the file begins.  You can use Control-C
  28.  *     to break out of the program during execution.
  29.  *     
  30.  *     The idea behind Emit was to create a useful tool for downloading
  31.  *     code to a target machine for testing.  For instance, in my
  32.  *     development enviroment, I compile my code on a Amiga 1000, then
  33.  *     zap the code over the serial port to an Amiga 500 for testing.
  34.  *     If the code bombs and the Guru visits, no sweat; my environment
  35.  *     on the 1000 is safe (sorta, I still use vdk: and make lots of
  36.  *     intermediate backups!).  The benefits of a dual-machine development
  37.  *     enviroment are terrific: I insure that the code works on both the
  38.  *     1000 and the 500, I have a fairly safe development environment,
  39.  *     and I can "Wack" the code through the port using the 1000 as a
  40.  *     debugging terminal when the going gets tough.  I would estimate
  41.  *     that moving to a dual-machine setup has doubled the efficiency
  42.  *     of my development time.  I use to do a lot of disk swapping
  43.  *     before writing Emit; I propose that this program will greatly
  44.  *     increase the lifespans of my disk-eject mechanisms.
  45.  *     
  46.  *     Emit was written very quick-and-dirty.  It shows.  I wrote it as
  47.  *     an experiment to see how fast Exec can handle the serial port,
  48.  *     and because there were no programs available that did what I
  49.  *     needed.  For instance, I tried using several terminal programs,
  50.  *     both commercial and public domain.  Only Online! 2.0 would perform
  51.  *     reliably at 19200 baud, and I felt ridiculous running a 176K
  52.  *     program to download a measly couple of bytes.  (BTW, Diga! NEVER
  53.  *     managed to do better than 4800 baud on ANY setting, according
  54.  *     to my stop-watch).  Also, the RKM 1 mentions that baud rates
  55.  *     up to 292,000 may be specified (though your mileage may vary),
  56.  *     so I was itching to try it.
  57.  *
  58.  *    Note that this code is *NOT* to be taken as a proper example of
  59.  *    how to control the serial port -- it merely works for me.  I do
  60.  *    several deplorable things for the sake of speed like busy-waiting,
  61.  *    disabling interrupts, poking and peeking at hardware registers,
  62.  *    and making unwarrented assumptions about the hardware that
  63.  *    SHOULD NEVER be done if you want your code to work with
  64.  *    future versions of the Amiga hardware.
  65.  *     
  66.  * FILES
  67.  *     Emit        * The executable code
  68.  *     emit.c        * Source to main routine, control module
  69.  *      ser.asm        * Source to assembly serial read/write routines
  70.  *      aprintf.asm    * Hand-rolled printf() replacement
  71.  *      macros.i    * Assembly includes
  72.  *     i.pre.h        * A kitchen-sink include file
  73.  *     makefile    * Makes it
  74.  *     
  75.  *     Written using Manx Aztec C 3.4b (thanks Jim!)
  76.  *     You might want to precompile i.pre.h to speed compiling.
  77.  *     
  78.  * DIAGNOSTICS
  79.  *     Emit has fairly good error detection and recovery facilities.
  80.  *     Typical problems that could crop up are bad filenames, file not
  81.  *     found, inability to open a file, insufficient RAM, serial port
  82.  *     problems, and serial flow control difficulties.  If a problem is
  83.  *     detected, Emit will exit and describe the problem, possibly with
  84.  *     a standard AmigaDOS error number or Exec serial problem number.
  85.  *     
  86.  * BUGS
  87.  *     Undoubtably.  Most of the known problems exist on the receive side
  88.  *     of the program.  The Emit sender sends the filesize to the Emit
  89.  *     receiver; the receiver gets only one chance to grab this
  90.  *    critical information.  No error-detection/correction/checksumming
  91.  *    is done on transfered data.  When receiving files, Emit creates
  92.  *    an input buffer the same size as the file to be downloaded; if
  93.  *    there is insufficient RAM for the buffer the transfer will fail.
  94.  *    I put an arbitrary 500K limit on the filesize that can be
  95.  *    downloaded -- feel free to change this if it annoys you.
  96.  *     I am being rather rude, crude, and high-handed when I access
  97.  *     the serial port, and while the program has proven reliable for
  98.  *     me, I can't promise there isn't some problem in there that might
  99.  *     bite one day.
  100.  *
  101.  *     Any comments, improvements, or discussion of techniques are
  102.  *     welcome.  You can reach me at:
  103.  *     
  104.  *         Justin V. McCormick
  105.  *         100 S. Meyers, Apt #1723
  106.  *         Lafayette, LA 70508
  107.  *         
  108.  *         Voice: 1-318-981-1314
  109.  *         PLINK: progress**
  110.  *         BIX:   dbrowning
  111.  *         
  112.  * -------------------------------------------------------------------- */
  113. #include "i.pre.h"
  114.  
  115. #define BAUD    280000L        /* Baud Rate                */
  116. #define BUFSIZE    8192L        /* Size of output buffer        */
  117.  
  118. /* Externs */
  119. extern int Enable_Abort;    /* We want to process ^C ourself    */
  120.  
  121. /* Global functions */
  122. char *extractfilename();
  123.  
  124. /* Global variables */
  125. int updownflag = 0;        /* 0 for upload, 1 for download        */
  126. char filename[300];        /* Filename string            */
  127. char progname[34];        /* What the program is called        */
  128. long filesize;            /* Byte length of file to upload    */
  129.  
  130. /* File related variables */
  131. struct FileHandle *emitfile = 0L; /* Pointer to stream opened        */
  132. struct FileInfoBlock *eFib = 0L;  /* General purpose Fib pointer    */
  133. struct FileLock *eLock = 0L;    /* FileLock pointer            */
  134. long buffersize = 0L;        /* Size of input buffer            */
  135. UBYTE *outbuf = 0L;        /* Pointer to input buffer        */
  136.  
  137. /* STUFF FOR SERIAL IO */
  138. #define SRPORTNAME "SerReadPort"
  139. #define SWPORTNAME "SerWritePort"
  140. ULONG S_Sig;            /* Signal masks for ports        */
  141.  
  142. /* globals functions */
  143. int   OpenSerial ();
  144. int   CloseSerial ();
  145.  
  146. /* global structures */
  147. struct IOExtSer *RReq = 0L;      /* Pointer to Read Request block      */
  148. struct IOExtSer *WReq = 0L;      /* Pointer to Write Request block     */
  149. struct MsgPort *SerRPort = 0L;  /* Serial Read Port                */
  150. struct MsgPort *SerWPort = 0L;  /* Serial Write Port                */
  151.  
  152. /* other globals */
  153. long  error = 0L;        /* global error return status         */
  154. ULONG openflags = 0L;        /* devices we opened             */
  155. UBYTE  aserinbuf[2];        /* asynchronous serial input buffer     */
  156. UBYTE  *inbuf = 0L;        /* private serial input buffer        */
  157. struct FileHandle *cliout;    /* Standard output filehandle        */
  158.  
  159. /* openflags defines */
  160. #define SRDEV_OPENED     1    /* set if opened read device        */
  161. #define SWDEV_OPENED    2    /* set if opened write device        */
  162.  
  163. /* -------------------------------------------------------------------- */
  164. /*                 MAIN CODE                */
  165. /* -------------------------------------------------------------------- */
  166. main (argc, argv)
  167.   int argc;
  168.   char **argv;
  169. {
  170.   register long status;
  171.   register long x;
  172.  
  173. /* Do our own ^C processing, thank you */
  174.   Enable_Abort = 0;
  175.  
  176. /* Where's output go? */
  177.   cliout = Output();
  178.   if (cliout == 0L)
  179.     CleanUp("");
  180.  
  181. /* Allocate a FileInfoBlock */
  182.   if ( (eFib = (struct FileInfoBlock *)AllocMem((long)sizeof(struct FileInfoBlock), MEMF_PUBLIC | MEMF_CLEAR)) == 0L)
  183.     CleanUp("No RAM");
  184.  
  185. /* Parse command line arguments */
  186.   strcpy(progname, extractfilename(argv[0]));
  187.  
  188.   if (argc < 3)
  189.     ShowUsage();    /* Not enough args */
  190.     
  191.   if (argv[1][0] != '-')
  192.     ShowUsage();    /* Option didn't start with a '-' */
  193.  
  194. /* Upload or Download? */
  195.   switch (argv[1][1])
  196.   {
  197.     case 's':
  198.     case 'S':
  199.       updownflag = 0;
  200.       break;
  201.     case 'r':
  202.     case 'R':
  203.       updownflag = 1;
  204.       break;
  205.     default:
  206.       ShowUsage();
  207.       break;
  208.   }
  209.  
  210. /* Copy off argv[2], assume it is filename */
  211.   strcpy(filename, argv[2]);
  212.  
  213. /* Open file */
  214.   if (updownflag == 1)    /* Create and open file for download */
  215.     emitfile = Open(filename, MODE_NEWFILE);
  216.   else
  217.   {
  218.   /* Allocate input buffer */
  219.     if ( (inbuf = (UBYTE*)AllocMem(BUFSIZE, MEMF_PUBLIC|MEMF_CLEAR)) == 0L)
  220.       CleanUp("No ram for buffer!");
  221.  
  222.   /* Grab a Lock the file */
  223.     if ( (eLock = (struct FileLock *)Lock(filename, ACCESS_READ)) == 0L)
  224.     {
  225.       CleanUp("can't lock '%s', IoErr %ld", filename, IoErr());
  226.     }
  227.  
  228.   /* Examine the root block */
  229.     if ( !Examine(eLock, eFib) )
  230.     {
  231.       CleanUp("can't examine '%s', IoErr %ld", filename, IoErr());
  232.     }
  233.     
  234.   /* Is it a file? */    
  235.     if (eFib->fib_DirEntryType >= 0L)
  236.     {
  237.       CleanUp("'%s' is a directory, not a file", filename);
  238.     }
  239.  
  240.   /* Grab the length */
  241.     filesize = eFib->fib_Size;
  242.  
  243.   /* Open the file for upload */
  244.     emitfile = Open(filename, MODE_OLDFILE);
  245.   
  246.   }
  247.  
  248.   if (emitfile == 0L)
  249.   {
  250.     CleanUp("can't open '%s', IoErr %ld", filename, IoErr() );
  251.   }
  252.  
  253. /* Do heavy Amiga Kernal Magic to set up serial I/O */
  254.   OpenSerial();
  255.  
  256. /* Do the first async read to initialize the read port */
  257.   GetASer();
  258.  
  259. /* Enter the control loop */
  260.   if (updownflag == 0)        /* Upload */
  261.     do_upload();
  262.   else                /* Download */
  263.     do_download();
  264.  
  265. /* Must be done */
  266.   CleanUp("done");
  267. }
  268.  
  269. /* -------------------------------------------------------------------- */
  270. /* Describe correct usage, cleanup                    */
  271. /* -------------------------------------------------------------------- */
  272. ShowUsage()
  273. {
  274.   aprintf("\x9b1;33;40mEmit V2.0\x9b0m -- 280,000 Baud Serial File Transfer Utility\n\
  275. \tFreeware by Justin V. McCormick 1/29/88\n\n\
  276.  \x9b1;33;40mUsage:\x9b0m %s -[s][r] [filename]\n\
  277. \t-s == send\n\
  278. \t-r == receive\n", progname);
  279.   CleanUp("");
  280. }
  281.  
  282. /*---------------------------- CleanUp -----------------------------------*/
  283. /* Close up everything and exit.
  284.  */
  285. CleanUp (msg, arg1, arg2, arg3)
  286.   char *msg, *arg1, *arg2, *arg3;
  287. {
  288.   if (inbuf)
  289.     FreeMem(inbuf, BUFSIZE);
  290.  
  291.   if (eLock)
  292.     UnLock(eLock);
  293.  
  294.   if (eFib)
  295.     FreeMem(eFib, (long)sizeof(struct FileInfoBlock));
  296.  
  297.   if (outbuf)
  298.     FreeMem(outbuf, buffersize);
  299.  
  300.   if (emitfile)
  301.     Close(emitfile);
  302.  
  303.   CloseSerial ();
  304.  
  305.   if (*msg)
  306.   {
  307.     aprintf ("\x9b1;33;40m%s:\x9b0m ", progname);
  308.     aprintf(msg, arg1, arg2, arg3);
  309.     aprintf(".\n");
  310.   }
  311.  
  312.   exit (0);
  313. }
  314.  
  315. /* -------------------------------------------------------------------- */
  316. /* Given a full pathname, return just the filename             */
  317. /* -------------------------------------------------------------------- */
  318. char *extractfilename(name)
  319.   register char *name;
  320. {
  321.   register char *tmp;
  322.  
  323.   tmp = rindex(name, '/');
  324.   if (!tmp)
  325.   {
  326.     tmp = rindex(name, ':');
  327.     if (!tmp)
  328.       tmp = name;
  329.     else
  330.       tmp++;
  331.   }
  332.   else
  333.     tmp++;
  334.   if (strlen(tmp) > 30)
  335.     tmp[30] = '\0';
  336.   return(tmp);
  337. }
  338.  
  339. /* -------------------------------------------------------------------- */
  340. /* Override Manx declaration, we will process ^C ourself        */
  341. /* -------------------------------------------------------------------- */
  342. Chk_Abort ()
  343. {
  344.   return (0);
  345. }
  346.  
  347. /* -------------------------------------------------------------------- */
  348. /* See if user hit ^C                            */
  349. /* -------------------------------------------------------------------- */
  350. CheckCntrlC()
  351. {
  352.  if (SetSignal (0L, 0L) & SIGBREAKF_CTRL_C)
  353.  {
  354.    aprintf("^C");
  355.    return (1);
  356.  }
  357.  else
  358.    return(0);
  359. }
  360.  
  361. /* -------------------------------------------------------------------- */
  362. /* Upload a file asap                            */
  363. /* -------------------------------------------------------------------- */
  364. do_upload()
  365. {
  366.   register long length;
  367.   long blksize = BUFSIZE;
  368.  
  369. /* Let the user in on what's happening */
  370.   aprintf("Filesize to upload: %ld bytes\n", filesize);
  371.   aprintf("Waiting for ENQ...");
  372.   fflush(stdout);
  373.  
  374. /* Might be some trash or ENQ's already, flush it out */
  375.   RReq->IOSer.io_Command = CMD_FLUSH;
  376.   if (DoIO (RReq) != 0L)
  377.     CleanUp ("Can't flush the serial device!");
  378.  
  379. /* Wait for ENQ character from dest */
  380.   while (1)
  381.   {
  382.     if (GotENQ())        /* Check for ENQ */
  383.       break;
  384.     if (CheckCntrlC())
  385.       CleanUp("upload aborted");
  386.     Delay(5L);
  387.   }
  388.   SerialWrite("\x06", 1L);        /* Send a ACK */
  389.  
  390.   aprintf("ENQ\nSending file...\n");
  391.   Delay(15L);
  392.  
  393.   SerialWrite(&filesize, 4L);
  394.  
  395.   Delay(25L);
  396.   aprintf("%ld\n", filesize);
  397.   
  398. /* Dump the file */
  399.   while (filesize)
  400.   {
  401.     if (filesize < blksize)
  402.       blksize = filesize;
  403.  
  404.     length = Read(emitfile, inbuf, blksize);
  405.     if (length != blksize)
  406.     {
  407.       CleanUp("can't read file, IoErr %ld", IoErr());
  408.     }
  409.  
  410.     SerialWrite(inbuf, blksize);
  411.  
  412.     filesize = filesize - blksize;
  413.     aprintf("\x9b\x46\x0d\x9b\x4b%ld\n", filesize);
  414.     if (CheckCntrlC())
  415.       CleanUp("upload aborted");
  416.   }
  417. }
  418.  
  419. /* -------------------------------------------------------------------- */
  420. /* Download a file pronto                        */
  421. /* -------------------------------------------------------------------- */
  422. do_download()
  423. {
  424.   register long length;
  425.   register UBYTE *bufp;
  426.   register long blksize;
  427.  
  428.   aprintf("Waiting for ACK...");
  429.   fflush(stdout);
  430.  
  431. /* Enquire of host at regular intervals, wait for ACK or abort */
  432.   while (1)
  433.   {
  434.     if (GotACK())        /* Got an ACK      */
  435.       break;
  436.  
  437.     if (CheckCntrlC())        /* Got an abort     */
  438.       CleanUp("Aborted");
  439.     
  440.     SerialWrite ("\x05", 1L);    /* Otherwise, send an ENQ */
  441.     Delay (10L);        /* wait 1/5th sec.    */
  442.   }
  443.   aprintf("ACK\n");
  444.  
  445. /* Get filesize to download */
  446.   SerialRead(&filesize, 4L);
  447.   if (filesize <= 0 || filesize > 500000L)
  448.   {
  449.     CleanUp("Filesize %ld out of range", filesize);
  450.   }
  451.   aprintf("Filesize to download: %ld bytes\n Reading data...\n", filesize);
  452.   fflush(stdout);
  453.  
  454. /* Allocate input buffer */
  455.   buffersize = filesize;
  456.   if ( (outbuf = AllocMem(buffersize, MEMF_PUBLIC | MEMF_CLEAR)) == 0L)
  457.     CleanUp("can't allocate input buffer");
  458.  
  459. /* Pull in the rest of the characters */
  460.   SerialRead(outbuf, filesize);
  461.  
  462. /* Write the buffer to file */
  463.   aprintf(" Writing file...\n");
  464.   Write(emitfile, outbuf, buffersize);
  465. }
  466.  
  467. /* -------------------------- OpenSerial ------------------------------ */
  468. /* Open the serial device twice, once for read and once for write.    */
  469. /* -------------------------------------------------------------------- */
  470. int OpenSerial ()
  471. {
  472.  
  473. /* Open the ports */
  474.   if ((SerRPort = CreatePort (SRPORTNAME, 0L)) == 0L ||
  475.       (SerWPort = CreatePort (SWPORTNAME, 0L)) == 0L)
  476.   {
  477.     CleanUp ("can't open SerRPort or SerWPort");
  478.   }
  479.  
  480. /* Create the request blocks */
  481.   if ((RReq = (struct IOExtSer *) CreateExtIO (SerRPort, (long) sizeof (*RReq))) == 0L ||
  482.       (WReq = (struct IOExtSer *) CreateExtIO (SerWPort, (long) sizeof (*WReq))) == 0L)
  483.   {
  484.     CleanUp ("can't open RReq or WReq");
  485.   }
  486.  
  487. /* Set io_SerFlags to share them ports */
  488.   RReq->io_SerFlags = WReq->io_SerFlags = SERF_SHARED;
  489.  
  490. /* Open the device once for each request block */
  491.   if ((error = OpenDevice (SERIALNAME, 0L, RReq, 0L)) != 0L)
  492.     CleanUp ("can't open read device!");
  493.   else
  494.     openflags |= SRDEV_OPENED;      /* Successfully opened read device */
  495.  
  496.   if ((error = OpenDevice (SERIALNAME, 0L, WReq, 0L)) != 0L)
  497.     CleanUp ("can't open write device!");
  498.   else
  499.     openflags |= SWDEV_OPENED;      /* Successfully opened write device */
  500.  
  501. /* Set our wakeup signal mask for the serial read port */
  502.   S_Sig = 1L << SerRPort->mp_SigBit;    
  503.  
  504. /* Set serial parameters */
  505.   RReq->io_Baud = BAUD;
  506.   WReq->io_Baud = BAUD;
  507.   RReq->io_RBufLen = 8000L;
  508.   WReq->io_RBufLen = 8000L;
  509.   RReq->io_ReadLen = (UBYTE)8;
  510.   WReq->io_ReadLen = (UBYTE)8;
  511.   RReq->io_WriteLen = (UBYTE)8;
  512.   WReq->io_WriteLen = (UBYTE)8;
  513.   RReq->io_StopBits = (UBYTE)1;
  514.   WReq->io_StopBits = (UBYTE)1;
  515.   RReq->io_SerFlags = SERF_SHARED | SERF_RAD_BOOGIE;
  516.   WReq->io_SerFlags = SERF_SHARED | SERF_RAD_BOOGIE;
  517.   RReq->IOSer.io_Command = SDCMD_SETPARAMS;
  518.   WReq->IOSer.io_Command = SDCMD_SETPARAMS;
  519.   if (DoIO(RReq) || DoIO(WReq))
  520.     CleanUp("can't set serial parameters");
  521.  
  522. }
  523.  
  524. /*----------------------------- CloseSerial -------------------------------*/
  525. int CloseSerial ()
  526. {
  527.   struct IOExtSer *tReq;
  528.  
  529.   if (RReq)
  530.   {
  531.     if (openflags & SRDEV_OPENED)
  532.     {
  533.       while ((tReq = (struct IOExtSer *) CheckIO (RReq)) == 0L)
  534.       {
  535.     AbortIO (RReq);
  536.     WaitIO (RReq);
  537.     GetMsg (SerRPort);
  538.       }
  539.       CloseDevice (RReq);
  540.     }
  541.     DeleteExtIO (RReq, (long)sizeof(*RReq) );
  542.   }
  543.  
  544.   if (WReq)
  545.   {
  546.     if (openflags & SWDEV_OPENED)
  547.       CloseDevice (WReq);
  548.     DeleteExtIO (WReq, (long)sizeof(*WReq) );
  549.   }
  550.   
  551.   if (SerRPort)
  552.     DeletePort (SerRPort);
  553.   if (SerWPort)
  554.     DeletePort (SerWPort);
  555. }
  556.  
  557. int GotACK()
  558. {
  559.   struct IOExtSer *tReq;
  560.   register char tempchar;
  561.  
  562.   if ((tReq = (struct IOExtSer *) CheckIO (RReq)) == 0L)
  563.     return (0L);
  564.  
  565.   WaitIO (RReq);
  566.   tempchar = aserinbuf[0] & '\x7f';
  567.   GetASer();
  568.  
  569.   if (tempchar == '\x06')
  570.     return(1);
  571.   else
  572.     return(0);
  573. }
  574.  
  575. int GotENQ()
  576. {
  577.   struct IOExtSer *tReq;
  578.   register char tempchar;
  579.  
  580.   if ((tReq = (struct IOExtSer *) CheckIO (RReq)) == 0L)
  581.     return (0L);
  582.  
  583.   WaitIO (RReq);
  584.   tempchar = aserinbuf[0] & '\x7f';
  585.   GetASer();
  586.  
  587.   if (tempchar == '\x05')
  588.     return(1);
  589.   else
  590.     return(0);
  591. }
  592.  
  593. /* --------------------------------- GetASer -------------------------- */
  594. /* Send a async read request to the serial port for 1 character        */
  595. /* -------------------------------------------------------------------- */
  596. GetASer ()
  597. {
  598.   RReq->IOSer.io_Command = CMD_READ;
  599.   RReq->IOSer.io_Length = 1L;
  600.   RReq->IOSer.io_Data = (APTR) &aserinbuf[0];
  601.   SendIO (RReq);
  602. }
  603.